package com.dhsc.ftp;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.pool2.ObjectPool;
import org.springframework.util.Assert;

import com.dhsc.exception.MyException;
import com.dhsc.util.CommonUtil;

import lombok.extern.slf4j.Slf4j;
import sun.misc.BASE64Decoder;

/**
 * @ClassName:  FTPUtil
 * @Description: FTP工具类
 * @date:   2024年8月19日 上午10:16:21       
 * @author: clong
 * @Copyright:  东海运营
 */
@Slf4j
public class FTPUtil
{
	/**
	 * ftpClient连接池初始化标志
	 */
	private static volatile boolean hasInit = false;
	
	private static String ISO = "ISO-8859-1";
	
	/**
	 * ftpClient连接池
	 */
	private static ObjectPool<FTPClient> ftpClientPool;

	private FTPUtil()
	{
	}

	/**
	 * @Title: init   
	 * @Description:初始化
	 * @author: wangxuebo
	 * @param :ftpClientPool      
	 * @return: void      
	 * @date:   2018年9月12日 下午1:44:45
	 */
	public static void init(ObjectPool<FTPClient> ftpClientPool)
	{
		if (!hasInit)
		{
			synchronized (FTPUtil.class)
			{
				if (!hasInit)
				{
					FTPUtil.ftpClientPool = ftpClientPool;
					hasInit = true;
				}
			}
		}
	}

	/**
	 * @Title: encoder   
	 * @Description: ftp编码转换
	 * @author: wangxuebo
	 * @param :path
	 * @throws: UnsupportedEncodingException  
	 * @return: String      
	 * @date:   2018年9月12日 上午9:06:33
	 */
	private static String encoder(String str) throws UnsupportedEncodingException
	{
		return new String(str.getBytes("GBK"), StandardCharsets.ISO_8859_1);
	}

	/**
	 * @Title: storeFile   
	 * @Description: 上传文件到ftp
	 * @author: wangxuebo
	 * @param :path FTP服务器保存目录(可能是多级)
	 * @param :filename 上传到FTP服务器上的文件名 
	 * @param :is 输入流
	 * @return :boolean 是否上传成功
	 * @throws :MyException
	 * @date:   2018年9月7日 下午3:00:01
	 */
	public static String storeFile(String path, String filename, String base64) throws IOException
	{
		FTPClient ftpClient = getFtpClient();
		InputStream is = null;
		String resultpath = "";
		try
		{
			//创建多级目录
			boolean resulta = mkdirPath(path, ftpClient);
			if(resulta)
			{
				ftpClient.enterLocalPassiveMode();
				ftpClient.changeWorkingDirectory(path);

				ftpClient.enterLocalPassiveMode();
				ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
				
				BASE64Decoder decoder = new BASE64Decoder(); 
			    byte[] bytes1 = decoder.decodeBuffer(base64);  
			    is = new ByteArrayInputStream(bytes1);
				
				boolean resultb = ftpClient.storeFile(encoder(filename), is);
				if(resultb)
				{
					resultpath = path;
				}
			}
			if(is != null)
			{
				is.close();
			}
		}
		catch (IOException e)
		{
			throw e;
		}
		finally
		{
			if(is != null)
			{
				is.close();
			}
			releaseFtpClient(ftpClient);
		}
		return resultpath;
	}
	
	/**
	 * @Title: mkdirPath   
	 * @Description: 创建文件夹
	 * @param path
	 * @param ftp
	 * @return boolean
	 * @author: chenlong
	 * @date:   2022年1月10日 下午3:21:07
	 */
	public static boolean mkdirPath(String path) {
		FTPClient ftpClient = getFtpClient();
		boolean result = false;
        try {
            String directory = path.endsWith("/") ? path : path + "/";
            if (!directory.equalsIgnoreCase("/") && !ftpClient.changeWorkingDirectory(new String(directory.getBytes("GBK"), StandardCharsets.ISO_8859_1))) {
                // 如果远程目录不存在，则递归创建远程服务器目录
                int start = 0;
                int end;
                if (directory.startsWith("/")) {
                    start = 1;
                }
                end = directory.indexOf("/", start);
                do {
                    String subDirectory = new String(path.substring(0, end).getBytes("GBK"), StandardCharsets.ISO_8859_1);
                    if (!ftpClient.changeWorkingDirectory(subDirectory)) {
                        if (ftpClient.makeDirectory(subDirectory)) {
                            if (!ftpClient.changeWorkingDirectory(subDirectory)) {
                                return false;
                            }
                        } else {
                            return false;
                        }
                    }
                    start = end + 1;
                    end = directory.indexOf("/", start);
                } while (end > start);
            }
            result = true;
        } catch (Exception exception) {
            exception.printStackTrace();
        }finally
		{
			releaseFtpClient(ftpClient);
		}
        return result;
    }
	
	/**
	 * @Title: mkdirPath   
	 * @Description: 创建文件夹
	 * @param path
	 * @param ftpClient
	 * @return boolean
	 * @author: clong
	 * @date:   2024年9月4日 下午3:21:53
	 */
	public static boolean mkdirPath(String path, FTPClient ftpClient) {
		boolean result = false;
        try {
            String directory = path.endsWith("/") ? path : path + "/";
            if (!directory.equalsIgnoreCase("/") && !ftpClient.changeWorkingDirectory(new String(directory.getBytes("GBK"), StandardCharsets.ISO_8859_1))) {
                // 如果远程目录不存在，则递归创建远程服务器目录
                int start = 0;
                int end;
                if (directory.startsWith("/")) {
                    start = 1;
                }
                end = directory.indexOf("/", start);
                do {
                    String subDirectory = new String(path.substring(0, end).getBytes("GBK"), StandardCharsets.ISO_8859_1);
                    if (!ftpClient.changeWorkingDirectory(subDirectory)) {
                        if (ftpClient.makeDirectory(subDirectory)) {
                            if (!ftpClient.changeWorkingDirectory(subDirectory)) {
                                return false;
                            }
                        } else {
                            return false;
                        }
                    }
                    start = end + 1;
                    end = directory.indexOf("/", start);
                } while (end > start);
            }
            result = true;
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return result;
    }

	/**
	 * @Title: deleteFile   
	 * @Description:删除ftp上的文件
	 * @author: wangxuebo
	 * @param :path(/uuid)
	 * @throws :IOException
	 * @date:   2018年9月10日 下午1:52:19
	 */
	public static boolean deleteFile(String ftpPath) throws Exception
	{
		FTPClient ftpClient = getFtpClient();
		try
		{
			return ftpClient.deleteFile(new String(ftpPath.getBytes("GBK"), StandardCharsets.ISO_8859_1));
		}
		catch (IOException e)
		{
			throw e;
		}
		finally
		{
			releaseFtpClient(ftpClient);
		}
	}

	/**
	 * @Title: retrieveFile   
	 * @Description:检索文件
	 * @author: wangxuebo
	 * @param :response
	 * @param :path
	 * @throws :IOException      
	 * @return: boolean      
	 * @date:   2018年9月12日 上午10:50:36
	 */
	public static boolean retrieveFile(HttpServletResponse response, String path) throws IOException
	{
		FTPClient ftpClient = getFtpClient();
		try
		{
			String fileName=CommonUtil.getFileNameByPath(path, true);
			response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(fileName, "UTF-8"));
			String encodePath = encoder(path);
			ftpClient.changeWorkingDirectory("/");
			OutputStream os = response.getOutputStream();
			return ftpClient.retrieveFile(encodePath, os);
		}
		catch (IOException e)
		{
			throw e;
		}
		finally
		{
			releaseFtpClient(ftpClient);
		}
	}

	private static FTPClient getFtpClient()
	{
		checkFtpClientPoolAvailable();
		FTPClient ftpClient = null;
		Exception ex = null;
		// 获取连接最多尝试3次
		for (int i = 0; i < 3; i++)
		{
			try
			{
				ftpClient = ftpClientPool.borrowObject();
				ftpClient.changeWorkingDirectory("/");
				break;
			}
			catch (Exception e)
			{
				ex = e;
			}
		}
		if (ftpClient == null)
		{
			throw new MyException("Could not get a ftpClient from the pool", ex);
		}
		ftpClient.setControlEncoding("GBK");
		FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
		conf.setServerLanguageCode("zh");
		return ftpClient;
	}

	private static void releaseFtpClient(FTPClient ftpClient)
	{
		if (ftpClient == null)
		{
			return;
		}

		try
		{
			ftpClientPool.returnObject(ftpClient);
		}
		catch (Exception e)
		{
			// destoryFtpClient
			if (ftpClient.isAvailable())
			{
				try
				{
					ftpClient.disconnect();
				}
				catch (IOException io)
				{
					log.error("ftpClient关闭异常", io);
				}
			}
			log.error("ftp连接池回收异常", e);
		}
	}

	private static void checkFtpClientPoolAvailable()
	{
		Assert.state(hasInit, "FTP未启用或连接失败！");
	}
	
	/**
	 * @Title: copyByFtp   
	 * @Description: 备份FTP文件夹及文件
	 * @param ftppath
	 * @param ftpcopypath
	 * @param sourceDir
	 * @return boolean
	 * @author: chenlong
	 * @date:   2022年1月10日 下午4:01:29
	 */
	public static void copyByFtp(String ftppath, String ftpcopypath, String sourceDir)
	{
		FTPClient ftpClient = getFtpClient();
    	try
		{
    		String pathb = ftpcopypath + "/" + CommonUtil.getSystemTime().substring(0,4) + "/" + CommonUtil.getSystemTime().substring(0,6) + "/"
    				+CommonUtil.getSystemTime().substring(0,8)+"/"+CommonUtil.getSystemTime();//复制后地址
    		// 新建目标目录  
    		if (!ftpClient.changeWorkingDirectory(pathb)) {
                // 创建多层目录
                mkdirPath(pathb, ftpClient);
            }
    		List<String> list = new ArrayList<>();
    		copyFolder(ftppath + "/" + sourceDir, pathb, sourceDir, ftpClient, list);
    		//删除备份后的源文件
    		if(CommonUtil.isNotEmptyList(list))
    		{
    			for(int i=list.size()-1;i>=0;i--)
    			{
    				if(list.get(i).contains("."))
    				{
    					ftpClient.deleteFile(new String(list.get(i).getBytes("GBK"),ISO));
    				}else{
    					ftpClient.removeDirectory(new String(list.get(i).getBytes("GBK"),ISO));
    				}
    			}
    		}
		}
		catch (Exception e)
		{
			log.error("文件移动留痕出错",e);
		}
		finally
		{
			releaseFtpClient(ftpClient);
		}
	}
	
    /**
     * @Title: copyFolder   
     * @Description: 备份文件夹
     * @param remotePath
     * @param localPath
     * @param fileName
     * @param ftpClient
     * @throws IOException void
     * @author: chenlong
     * @date:   2022年1月11日 下午1:19:22
     */
  	public static void copyFolder(String remotePath,String localPath,String fileName, FTPClient ftpClient, List<String> list) throws IOException{
  		try {
  			list.add(remotePath);
	  		if (remotePath!=null && !remotePath.equals("")) {
	  			localPath = localPath+"/"+fileName;
				if (!ftpClient.changeWorkingDirectory(localPath)) {
	                // 创建多层目录
	                mkdirPath(localPath, ftpClient);
	            }
	  			//获得FTPFile对象数组
	  			FTPFile[] ftpFiles = ftpClient.listFiles(new String(remotePath.getBytes("GBK"),ISO));
	  			if (ftpFiles!=null && ftpFiles.length>0) {
	  				for (int i = 0; i < ftpFiles.length; i++) {
	  					FTPFile subFile = ftpFiles[i];
	  					//判断是否为目录结构
	  					if (subFile.isDirectory()) {
	  						//如果为目录结构
	  						//调用自身方法，进行下一层级目录循环
	  						copyFolder(remotePath+"/"+subFile.getName(), localPath, subFile.getName(), ftpClient, list);
	  					} else {
	  						//调用下载方法对文件进行下载
	  						list.add(remotePath + "/" + subFile.getName());
	  						copyFile(subFile.getName(), remotePath, localPath, ftpClient);
	  					}
	  				}
	  			}
	  		}
  		}catch (IOException e)
		{
        	log.error("FTP备份文件夹出错",e);
		}
  	}
  	
  	/**
  	 * @Title: copyFile   
  	 * @Description: 备份文件
  	 * @param sourceFileName
  	 * @param patha
  	 * @param pathb
  	 * @param ftpClient
  	 * @return
  	 * @throws IOException boolean
  	 * @author: chenlong
  	 * @date:   2022年1月11日 下午1:19:02
  	 */
  	public static void copyFile(String sourceFileName, String patha, String pathb, FTPClient ftpClient) throws IOException {
    	ByteArrayInputStream in = null;
        
        try (ByteArrayOutputStream fos = new ByteArrayOutputStream();){
        	if (!ftpClient.changeWorkingDirectory(pathb)) {
            	mkdirPath(pathb, ftpClient);
            }
            ftpClient.changeWorkingDirectory(patha);// 变更工作路径   
            ftpClient.enterLocalPassiveMode();
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);// 设置以二进制流的方式传输
            ftpClient.retrieveFile(new String((patha + "/" + sourceFileName).getBytes("GBK"), ISO), fos);// 将文件读到内存中
            in = new ByteArrayInputStream(fos.toByteArray());
            ftpClient.changeWorkingDirectory(pathb);
            ftpClient.storeFile(new String(sourceFileName.getBytes("GBK"), ISO), in);
        }catch (IOException e)
		{
        	log.error("FTP备份文件出错",e);
		}finally {
            // 关闭流  
            if (in != null) {
                in.close();
            }
        }
    }
    
	/**
	 * @Title: copyHisFile   
	 * @Description: 复制未经处理的原文件
	 * @param filepath 文件路径
	 * @param ori 原文件后缀  
	 * @throws IOException void
	 * @author: chenlin
	 * @date:   2020年11月26日 下午5:46:35
	 */
	public static void copyHisFile(String filepath,String ori) throws IOException {
		try {
			int num = filepath.lastIndexOf("/");
			String parentpath = filepath.substring(0,num+1);
			String filename = filepath.substring(num+1,filepath.length());
			String[] arr = filename.split("\\.");
			File source = new File(filepath);
			File dest = new File(parentpath+arr[0]+ori+"."+arr[1]);
			if(dest.exists()){
				dest.delete();
			}
			Files.copy(source.toPath(), dest.toPath()); 
		} catch (Exception e) {
			log.error("复制文件留痕出错", e);
		}
	}
	
	public static void downloadFtpFile(String ftpPath, String localPath, String fileName) throws IOException {
		FTPClient ftpClient = getFtpClient();
		OutputStream os = null;
		try {
			ftpClient.changeWorkingDirectory(new String(ftpPath.getBytes("GBK"), StandardCharsets.ISO_8859_1));
			File localFile = new File(localPath);
			if(!localFile.exists() && !localFile.isDirectory()) {
				localFile.mkdirs();
			}
			os = new FileOutputStream(localPath + "/" + fileName);
			ftpClient.retrieveFile(fileName, os);
			os.close();
		} catch (FileNotFoundException e) {
			log.error("没有找到" + ftpPath + "文件");
		} catch (SocketException e) {
			log.error("连接FTP失败.");
		} catch (IOException e) {
			log.error("文件读取错误。");
		}finally {
			if(os != null)
			{
				os.close();
			}
            releaseFtpClient(ftpClient);
        }
	}
	
	/**
	 * @Title: downloadFtpFiles   
	 * @Description: 下载FTP文件夹
	 * @param ftpPath
	 * @param localPath
	 * @throws IOException void
	 * @author: clong
	 * @date:   2024年8月19日 上午11:17:14
	 */
	public static void downloadFtpFiles(String ftpPath, String localPath) throws IOException {
		FTPClient ftpClient = getFtpClient();
		OutputStream os = null;
		try {
			ftpClient.changeWorkingDirectory(new String(ftpPath.getBytes("GBK"), StandardCharsets.ISO_8859_1));
			FTPFile[] ftpFiles = ftpClient.listFiles(new String(ftpPath.getBytes("GBK"),ISO));
			if(ftpFiles.length > 0)
			{
				File localFile = new File(localPath);
				if(!localFile.exists() && !localFile.isDirectory()) {
					localFile.mkdirs();
				}
				
				for(FTPFile file : ftpFiles)
				{
					if(file.isFile())
					{
						os = new FileOutputStream(localPath + "/" + file.getName());
						ftpClient.retrieveFile(file.getName(), os);
					}
				}
				os.close();
			}
		} catch (FileNotFoundException e) {
			log.error("没有找到" + ftpPath + "文件");
		} catch (SocketException e) {
			log.error("连接FTP失败.");
		} catch (IOException e) {
			log.error("文件读取错误。");
		}finally {
			if(os != null)
			{
				os.close();
			}
            releaseFtpClient(ftpClient);
        }
	}

}
